home *** CD-ROM | disk | FTP | other *** search
- /* $Header: main.c,v 2.12 90/03/07 11:00:58 chip Exp $
- *
- * A program to deliver local mail with some flexibility.
- *
- * $Log: main.c,v $
- * Revision 2.12 90/03/07 11:00:58 chip
- * Debugging messages only when verbose. (Sigh.)
- *
- * Revision 2.11 90/03/06 13:11:53 chip
- * Run error delivery file after normal delivery;
- * then call delivery routines again afterward.
- *
- * Revision 2.10 90/03/06 12:21:13 chip
- * Move logging into log.c and address parsing into addr.c.
- * New: error delivery file for messages that fail.
- * Major rearrangement of delivery file code.
- *
- * Revision 2.9 90/02/23 15:05:15 chip
- * Clean up the setuid/setgid renunciation logic.
- *
- * Revision 2.8 90/02/23 14:16:47 chip
- * Support "#!" in delivery files.
- * Support "user|program" and "user?error" from delivery files.
- * Improve debugging and error message formatting.
- * Rearrange code for clarity.
- *
- * Revision 2.7 90/02/06 11:56:40 chip
- * Enforce MBX_MODE regardless of UMASK.
- * Enforce ordered logging with a log lockfile.
- * Revise log format.
- *
- * Revision 2.6 89/12/19 16:45:26 network
- * Include <time.h> for the functions.
- *
- * Revision 2.5 89/12/19 16:29:32 network
- * Make timezone handling portable to System V.
- *
- * Revision 2.4 89/11/10 12:23:55 network
- * Handle recursion. Enhance logging.
- *
- * Revision 2.3 89/11/01 12:19:02 network
- * Delintify.
- *
- * Revision 2.2 89/11/01 11:51:47 network
- * Add logging.
- *
- * Revision 2.1 89/06/09 12:25:32 network
- * Update RCS revisions.
- *
- * Revision 1.13 89/06/09 12:23:53 network
- * Baseline for 2.0 release.
- *
- */
-
- #include "deliver.h"
- #include "patchlevel.h"
- #include <signal.h>
-
- /*
- * External data.
- */
-
- /* Variables set by getopt() [blech] */
-
- extern int optind, opterr;
- extern char *optarg;
-
- /*
- * Local data
- */
-
- static char dfl_sys[] = SYS_DELIVER;
- static char dfl_post[] = POST_DELIVER;
- static char dfl_err[] = ERR_DELIVER;
- static char dfl_user[] = USER_DELIVER;
-
- /*
- * Global data
- */
-
- int verbose = FALSE;
- int dryrun = FALSE;
- int rundfiles = TRUE;
- int printaddrs = FALSE;
- int leavetemps = FALSE;
- int boxdelivery = FALSE;
-
- char *progname = "deliver";
- char version[32] = "2.0";
- char *shell = SHELL;
-
- int rec_level = 0;
- int rec_parent = -1;
- char *sys_deliver = dfl_sys;
- char *post_deliver = dfl_post;
- char *err_deliver = dfl_err;
- char *user_deliver = dfl_user;
- char *sender = NULL;
- char *hostname = NULL;
-
- int eff_uid = -1;
- int eff_gid = -1;
- int real_uid = -1;
- int real_gid = -1;
-
- CONTEXT *eff_ct = NULL;
- CONTEXT *real_ct = NULL;
-
- int trust_user = FALSE;
- int trust_delfiles = FALSE;
- FILE *log = NULL;
- char *logfile = NULL;
- FILE *errlog = NULL;
- char *errlogfile = NULL;
-
- int tty_input = FALSE;
- SIGFLAG got_sig = FALSE;
-
- char *ttype[T_MAX] = { "header", "body", "header copy", "body copy" };
- char *tfile[T_MAX] = { NULL, NULL, NULL, NULL };
- char *tenv[T_MAX] = { NULL, NULL, ENV_HEADER, ENV_BODY };
- int tfd[T_MAX] = { -1, -1, -1, -1 };
-
- /*
- * Local functions.
- */
-
- static SIGTYPE sighup(), sigint(), sigquit();
-
- /*----------------------------------------------------------------------
- * The Program.
- */
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- char *p;
- int i, c, errcount, copy;
-
- /* Make sure that stdout and stderr are interleaved correctly */
-
- Linebuf(stdout);
- Linebuf(stderr);
-
- /* Figure out the name used to invoke this program. */
-
- progname = basename(argv[0]);
-
- /* Special hack -- handle the recursion level and parent first. */
-
- if ((p = getenv(ENV_DLEVEL)) != NULL && (i = atoi(p)) > 0)
- rec_level = i;
-
- if ((p = getenv(ENV_DPID)) != NULL && (i = atoi(p)) > 0)
- rec_parent = i;
-
- /* If recursion level is non-zero, append it to the program name. */
-
- if (rec_level > 0)
- {
- char *np;
-
- np = zalloc((unsigned) strlen(progname) + 16);
- (void) sprintf(np, "%s[%d]", progname, rec_level);
- progname = np;
- }
-
- /* What version of the program is this? */
-
- (void) sprintf(version + strlen(version), ".%02d", PATCHLEVEL);
-
- /* Let's be sane about the file creation mask. */
-
- (void) umask(UMASK);
-
- /* Find effective and real uids and gids. */
-
- eff_uid = geteuid();
- eff_gid = getegid();
- real_uid = getuid();
- real_gid = getgid();
-
- /* Make sure that setuidness is reasonable. */
-
- if (eff_uid != real_uid && eff_uid != 0)
- {
- error("if setuid, must be setuid root\n");
- leave(1);
- }
-
- /* Process environment: handle recursive invocation. */
-
- if ((p = getenv(ENV_DFLAGS)) != NULL)
- {
- while (*p)
- {
- switch (*p++)
- {
- case 'v':
- verbose = TRUE;
- break;
- case 'd':
- verbose = TRUE;
- dryrun = TRUE;
- break;
- case 'A':
- printaddrs = TRUE;
- dryrun = TRUE;
- break;
- case 'n':
- rundfiles = FALSE;
- break;
- case 't':
- leavetemps = TRUE;
- break;
- }
- }
- }
-
- if ((p = getenv(ENV_SYSDEL)) != NULL && *p)
- sys_deliver = p;
- if ((p = getenv(ENV_POSTDEL)) != NULL && *p)
- post_deliver = p;
- if ((p = getenv(ENV_ERRDEL)) != NULL && *p)
- err_deliver = p;
- if ((p = getenv(ENV_USERDEL)) != NULL && *p)
- user_deliver = p;
-
- if ((p = getenv(ENV_SENDER)) != NULL && *p)
- sender = p;
- if ((p = getenv(ENV_HOSTNAME)) != NULL && *p)
- hostname = p;
-
- /* Parse command line arguments */
-
- while ((c = getopt(argc, argv, "vdAntbs:p:e:u:r:h:")) != EOF)
- {
- switch (c)
- {
- case 'v':
- verbose = TRUE;
- break;
- case 'd':
- verbose = TRUE;
- dryrun = TRUE;
- break;
- case 'A':
- printaddrs = TRUE;
- dryrun = TRUE;
- break;
- case 'n':
- rundfiles = FALSE;
- break;
- case 't':
- leavetemps = TRUE;
- break;
- case 'b':
- boxdelivery = TRUE;
- break;
- case 's':
- if (*optarg)
- sys_deliver = optarg;
- break;
- case 'p':
- if (*optarg)
- post_deliver = optarg;
- break;
- case 'e':
- if (*optarg)
- err_deliver = optarg;
- break;
- case 'u':
- if (*optarg)
- user_deliver = optarg;
- break;
- case 'r':
- if (*optarg)
- sender = optarg;
- break;
- case 'h':
- if (*optarg)
- hostname = optarg;
- break;
- case '?':
- usage();
- }
- }
-
- /* Open temporary log files. */
-
- openlogs();
-
- /* Figure out the name of this host, unless we've been told. */
-
- if (!hostname && (hostname = gethost()) == NULL)
- {
- hostname = "unknown";
- error("unable to determine host name; using \"%s\"\n",
- hostname);
- }
-
- /* If no destinations were given, forget it. */
-
- if (optind >= argc)
- {
- error("no recipients specified\n");
- usage();
- }
-
- /* If debugging, print a nice banner. */
-
- if (verbose)
- {
- message("%s %s running on host %s\n",
- progname, version, hostname);
- }
-
- /*
- * Renounce setuid privileges if:
- * 1. We don't trust the real user id, OR
- * 2. The caller is using non-default delivery file names.
- */
-
- if (trusted_uid(real_uid))
- trust_user = TRUE;
-
- if (strcmp(dfl_sys, sys_deliver) == 0
- && strcmp(dfl_post, post_deliver) == 0
- && strcmp(dfl_err, err_deliver) == 0
- && strcmp(dfl_user, user_deliver) == 0)
- trust_delfiles = TRUE;
-
- if ((!trust_user && !trust_delfiles)
- && (eff_uid != real_uid || eff_gid != real_gid))
- {
- if (setuid(real_uid) == -1)
- {
- syserr("can't renounce setuid privileges");
- leave(1);
- }
- if (setgid(real_gid) == -1)
- {
- syserr("can't renounce setgid privileges");
- leave(1);
- }
-
- if ((eff_uid = geteuid()) != real_uid
- || (eff_gid = getegid()) != real_gid)
- {
- error("kernel bug: can't renounce setuid/setgid");
- leave(1);
- }
-
- if (verbose)
- message("renounced setuid privileges\n");
- }
-
- /* Get the contexts of our effective and real uids. */
-
- if ((eff_ct = uid_context(eff_uid)) == NULL)
- error("invalid effective uid %d!?\n", eff_uid);
-
- if ((real_ct = uid_context(real_uid)) == NULL)
- error("invalid real uid %d!?\n", real_uid);
-
- if (!eff_ct || !real_ct)
- leave(1);
-
- if (verbose)
- {
- message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n",
- eff_ct->ct_name, eff_ct->ct_uid, eff_ct->ct_gid,
- real_ct->ct_name, real_ct->ct_uid, real_ct->ct_gid);
- }
-
- /*
- * Where is the message coming from?
- */
-
- if (isatty(0))
- tty_input = TRUE;
-
- /*
- * If we are not going to deliver, or if we are receiving the
- * message from a tty, catch signals so we can remove temp files.
- * Otherwise, ignore signals.
- */
-
- if (dryrun || tty_input)
- catch_sigs();
- else
- ignore_sigs();
-
- /*
- * Create the temporary files and write the message to them.
- */
-
- copy = copy_message();
-
- /*
- * No more signals...
- */
-
- ignore_sigs();
-
- /*
- * ... but if we had already caught a signal,
- * or if copy_msg() had a problem, leave.
- */
-
- if ((copy < 0) || got_sig)
- {
- if (got_sig)
- error("caught signal - exiting\n");
- leave(1);
- }
-
- /*
- * Set up useful environment variables.
- * Note that this must be done _after_ copy_message(),
- * since that's where the temp files are created.
- */
-
- setup_environ();
-
- /*
- * If recursion is too deep, consider mail undeliverable.
- */
-
- if (rec_level > REC_LIMIT)
- main_toodeep();
-
- /*
- * Else if the "-b" flag was specified, arguments are mailboxes.
- */
-
- else if (boxdelivery)
- main_boxes(argc - optind, argv + optind);
-
- /*
- * Otherwise, arguments are addresses.
- */
-
- else
- main_addrs(argc - optind, argv + optind);
-
- /*
- * Do all delivery.
- * If anything happens, print a debugging message.
- */
-
- i = 0;
- i += (mbox_deliver() > 0);
- i += (prog_deliver() > 0);
- i += (uucp_deliver() > 0);
- if (i > 0 && verbose)
- dumpdests("after delivery");
-
- /*
- * If we're not in mailbox mode, and if the error delivery file
- * executes, deliver to any destination(s) that it generated.
- */
-
- if (!boxdelivery
- && (i = err_dfile()) > 0)
- {
- if (verbose)
- dumpdests("after running error delivery file");
-
- i = 0;
- i += (mbox_deliver() > 0);
- i += (prog_deliver() > 0);
- i += (uucp_deliver() > 0);
- if (i > 0 && verbose)
- dumpdests("after delivery to error destinations");
- }
-
- /*
- * Report all results in log file.
- */
-
- logreport(argc - optind, argv + optind);
-
- /*
- * Report any errors.
- */
-
- errcount = report_errors(argc - optind, argv + optind);
-
- /*
- * All done.
- */
-
- leave(errcount ? 1 : 0);
- /* NOTREACHED */
- }
-
- /*----------------------------------------------------------------------
- * Normal address handling.
- * Call system delivery file, user delivery files,
- * post-user delivery file, error delivery file.
- */
-
- main_addrs(ac, av)
- int ac;
- char **av;
- {
- int n;
-
- /* Run all destinations though the system delivery file. */
-
- if (sys_dfile(ac, av) >= 0)
- {
- if (verbose)
- dumpdests("after running system delivery file");
- }
- else
- {
- int a;
-
- /*
- * System delivery file is missing or ignored.
- * Use the argument list verbatim.
- */
-
- for (a = 0; a < ac; ++a)
- (void) addr_dest(av[a], (CONTEXT *)NULL);
-
- if (verbose)
- dumpdests("as taken from argument list");
- }
-
- /*
- * Run each user destination through his delivery file.
- */
-
- n = user_dfiles();
- if (n > 0 && verbose)
- dumpdests("after running user delivery files");
-
- /*
- * Run each remaining destination though the post-user
- * delivery file.
- */
-
- n = post_dfile();
- if (n > 0 && verbose)
- dumpdests("after running post-user delivery file");
- }
-
- /*----------------------------------------------------------------------
- * Consider all arguments as mailbox filenames.
- */
-
- main_boxes(ac, av)
- int ac;
- char **av;
- {
- int a;
-
- if (verbose)
- message("mailbox delivery as %s\n", real_ct->ct_name);
-
- for (a = 0; a < ac; ++a)
- (void) dest(real_ct->ct_name, CL_MBOX, av[a]);
-
- if (verbose)
- dumpdests("(should all be mailboxes)");
- }
-
- /*----------------------------------------------------------------------
- * Recursion too deep. Bail out.
- */
-
- main_toodeep()
- {
- error("recursion limit (%d) exceeded; writing to %s:%s",
- REC_LIMIT, eff_ct->ct_name, MBX_UNDEL);
-
- dest_undel(eff_ct->ct_name);
- }
-
- /*----------------------------------------------------------------------
- * Print a usage message and exit.
- */
-
- usage()
- {
- message("Usage: %s [-b][-A][-d][-v][-n][-t][-r from][-h host] args\n", progname);
- message("-b All arguments are mailbox filenames.\n");
- message(" (Default: arguments are user names.)\n");
- message("-A Resolve addresses but do not deliver.\n");
- message("-d Be verbose but do not deliver.\n");
- message("-v Be verbose and deliver.\n");
- message("-n Do not run any delivery files.\n");
- message("-t Do not remote temp files before exiting.\n");
- message("-s file Specify the system delivery filename.\n");
- message("-p file Specify the post-user delivery filename.\n");
- message("-e file Specify the error delivery filename.\n");
- message("-u file Specify the user delivery filename.\n");
- message("-r from Specify the address to appear in the \"From \" line.\n");
- message("-h host Specify the host name.\n");
- message("args Either user addresses or mailboxes (-b).\n");
- leave(1);
- }
-
- /*----------------------------------------------------------------------
- * Clean up and exit.
- */
-
- leave(code)
- int code;
- {
- /* Report vital statistics if something went wrong. */
-
- errinfo();
-
- /* Discard temporary files. */
-
- if (! leavetemps)
- {
- int t;
-
- for (t = 0; t < T_MAX; ++t)
- {
- if (tfd[t] != -1)
- (void) close(tfd[t]);
- if (tfile[t] && unlink(tfile[t]) == -1)
- syserr("can't unlink %s", tfile[t]);
- }
- }
-
- /* Save temporary logs by appending to real logfiles. */
-
- savelogs();
-
- /* Discard temporary logs unless user requested otherwise. */
-
- if (!leavetemps)
- tosslogs();
-
- /* "I am outa here." */
-
- exit(code);
- }
-
- /*----------------------------------------------------------------------
- * Catch signals.
- */
-
- catch_sigs()
- {
- if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
- (void) signal(SIGHUP, sighup);
- if (signal(SIGINT, SIG_IGN) != SIG_IGN)
- (void) signal(SIGINT, sigint);
- if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
- (void) signal(SIGQUIT, sigquit);
- }
-
- /*----------------------------------------------------------------------
- * Ignore signals.
- */
-
- ignore_sigs()
- {
- (void) signal(SIGHUP, SIG_IGN);
- (void) signal(SIGINT, SIG_IGN);
- (void) signal(SIGQUIT, SIG_IGN);
- }
-
- static SIGTYPE
- sighup()
- {
- (void) signal(SIGHUP, sighup);
- got_sig = TRUE;
- }
-
- static SIGTYPE
- sigint()
- {
- (void) signal(SIGINT, sigint);
- got_sig = TRUE;
- }
-
- static SIGTYPE
- sigquit()
- {
- (void) signal(SIGQUIT, sigquit);
- got_sig = TRUE;
- }
-
- /*----------------------------------------------------------------------
- * Report any errors to stderr.
- * Return an error count.
- */
-
- int
- report_errors(ac, av)
- int ac;
- char **av;
- {
- DEST **dv;
- int i, count, errcount;
-
- if ((dv = dest_array(&count)) == NULL)
- return 0;
-
- errcount = 0;
- for (i = 0; i < count; ++i)
- {
- DEST *d;
- char *e;
- int len;
-
- d = dv[i];
-
- if (d->d_state != ST_ERROR)
- continue;
-
- if (++errcount == 1)
- {
- int a;
-
- error("delivery error on host %s.\n", hostname);
- message("Delivery to %s address%s failed:\n",
- ac ? "these" : "this",
- ac ? "es" : "");
- for (a = 0; a < ac; ++a)
- message("\t%s\n", av[a]);
- message("Reason(s) for failure:\n");
- }
-
- message("\t\"%s\"", d->d_name);
- if (d->d_class == CL_MBOX)
- message(", mailbox \"%s\"", d->d_param);
- else if (d->d_class == CL_PROG)
- message(", program \"%s\"", d->d_param);
-
- e = derrmsg(d);
- len = strlen(d->d_name) + strlen(e);
- if (d->d_param)
- len += strlen(d->d_param);
- message(":%s%s\n", (len > 60) ? "\n\t\t" : " ", derrmsg(d));
- }
-
- free((char *) dv);
-
- return errcount;
- }
-
- /*----------------------------------------------------------------------
- * Is the given uid trusted?
- */
-
- int
- trusted_uid(uid)
- int uid;
- {
- CONTEXT *ct;
- char **n;
- static char *t[] = { TRUSTED_USERS, 0 };
-
- for (n = t; *n; ++n)
- {
- if ((ct = name_context(*n)) != NULL && uid == ct->ct_uid)
- return TRUE;
- }
-
- return FALSE;
- }
-
- /*----------------------------------------------------------------------
- * Set up useful environment variables.
- */
-
- setup_environ()
- {
- char s[8];
- int f = 0;
-
- (void) sprintf(s, "%d", getpid());
- alloc_env(ENV_DPID, s);
-
- (void) sprintf(s, "%d", rec_level + 1);
- alloc_env(ENV_DLEVEL, s);
-
- s[f++] = '-';
- if (verbose)
- s[f++] = (dryrun ? 'd' : 'v');
- if (printaddrs)
- s[f++] = 'A';
- if (leavetemps)
- s[f++] = 't';
- s[f] = 0;
- alloc_env(ENV_DFLAGS, (f > 1) ? s : "");
-
- if (sys_deliver && *sys_deliver)
- alloc_env(ENV_SYSDEL, sys_deliver);
- if (user_deliver && *user_deliver)
- alloc_env(ENV_USERDEL, user_deliver);
- if (hostname && *hostname)
- alloc_env(ENV_HOSTNAME, hostname);
- if (sender && *sender)
- alloc_env(ENV_SENDER, sender);
-
- alloc_env("IFS", " \t\n");
- del_env("ENV"); /* in case SHELL is ksh */
- }
-